1 module spirv_cross; 2 3 import n = spirv_cross.native; 4 static import spv; 5 6 class ScCompilationError : Exception 7 { 8 this(string msg) 9 { 10 super(msg); 11 } 12 } 13 14 class ScError : Exception 15 { 16 this(string msg) 17 { 18 super(msg); 19 } 20 } 21 22 struct SPIRType; 23 24 struct Resource 25 { 26 uint id; 27 uint typeId; 28 uint baseTypeId; 29 string name; 30 } 31 32 struct ShaderResources 33 { 34 Resource[] uniformBuffers; 35 Resource[] storageBuffers; 36 Resource[] stageInputs; 37 Resource[] stageOutputs; 38 Resource[] subpassInputs; 39 Resource[] storageImages; 40 Resource[] sampledImages; 41 Resource[] atomicCounters; 42 Resource[] pushConstantBuffers; 43 Resource[] separateImages; 44 Resource[] separateSamplers; 45 } 46 47 struct EntryPoint 48 { 49 string name; 50 spv.ExecutionModel executionModel; 51 } 52 53 struct CombinedImageSampler 54 { 55 /// The ID of the sampler2D variable. 56 uint combinedId; 57 /// The ID of the texture2D variable. 58 uint imageId; 59 /// The ID of the sampler variable. 60 uint samplerId; 61 } 62 63 struct SpecializationConstant 64 { 65 /// The ID of the specialization constant. 66 uint id; 67 /// The constant ID of the constant, used in Vulkan during pipeline creation. 68 uint constant_id; 69 } 70 71 struct BufferRange 72 { 73 uint index; 74 size_t offset; 75 size_t range; 76 } 77 78 /// GLSL precision 79 enum GlslPrecision 80 { 81 dontCare, 82 low, 83 medium, 84 high, 85 } 86 87 /// Options for the GLSL compiler 88 struct ScOptionsGlsl 89 { 90 /// The shading language version. Corresponds to #version $VALUE. 91 uint ver = 450; 92 93 /// Emit the OpenGL ES shading language instead of desktop OpenGL. 94 bool es = false; 95 96 /// Debug option to always emit temporary variables for all expressions. 97 bool forceTemporary = false; 98 99 /// If true, Vulkan GLSL features are used instead of GL-compatible features. 100 /// Mostly useful for debugging SPIR-V files. 101 bool vulkanSemantics = false; 102 103 /// If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders. 104 /// The members of gl_PerVertex is determined by which built-ins are declared by the shader. 105 /// This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension 106 /// (EXT_shader_io_blocks) which makes things a bit more fuzzy. 107 bool separateShaderObjects = false; 108 109 /// Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays, 110 /// e.g. float foo[a * b * c]. 111 /// This function does not change the actual SPIRType of any object. 112 /// Only the generated code, including declarations of interface variables are changed to be single array dimension. 113 bool flattenMultidimensionalArrays = false; 114 115 /// For older desktop GLSL targets than version 420, the 116 /// GL_ARB_shading_language_420pack extensions is used to be able to support 117 /// layout(binding) on UBOs and samplers. 118 /// If disabled on older targets, binding decorations will be stripped. 119 bool enable420PackExtension = true; 120 121 /// GLSL: In vertex shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style). 122 // MSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. 123 // HLSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. 124 bool vertFixupClipspace = false; 125 126 /// Inverts gl_Position.y or equivalent. 127 bool vertFlipY = false; 128 129 /// If true, the backend will assume that InstanceIndex will need to apply 130 /// a base instance offset. Set to false if you know you will never use base instance 131 /// functionality as it might remove some internal uniforms. 132 bool vertSupportNonzeroBaseInstance = true; 133 134 /// Add precision mediump float in ES targets when emitting GLES source. 135 GlslPrecision fragDefaultFloatPrecision = GlslPrecision.medium; 136 /// Add precision highp int in ES targets when emitting GLES source. 137 GlslPrecision fragDefaultIntPrecision = GlslPrecision.medium; 138 } 139 140 private void scEnforce(n.ScResult res, string msg) 141 { 142 final switch (res) 143 { 144 case n.ScResult.success: 145 return; 146 case n.ScResult.compilationError: 147 throw new ScCompilationError(msg); 148 case n.ScResult.error: 149 throw new ScError(msg); 150 case n.ScResult.unhandled: 151 throw new Exception(msg); 152 } 153 } 154 155 private void scEnforce(const(n.ScCompiler)* cl, n.ScResult res) 156 { 157 final switch (res) 158 { 159 case n.ScResult.success: 160 return; 161 case n.ScResult.compilationError: 162 throw new ScCompilationError(n.sc_compiler_get_error_string(cl)); 163 case n.ScResult.error: 164 throw new ScError(n.sc_compiler_get_error_string(cl)); 165 case n.ScResult.unhandled: 166 throw new Exception(n.sc_compiler_get_error_string(cl)); 167 } 168 } 169 170 /// Abstract SPIR-V cross compiler 171 /// Analyses and provides introspection into SPIR-V byte code 172 abstract class ScCompiler 173 { 174 private n.ScCompiler* _cl; 175 176 private this(n.ScCompiler* cl) 177 { 178 _cl = cl; 179 } 180 181 ~this() 182 { 183 dispose(); 184 } 185 186 /// Dispose native resources held by the compiler. 187 /// It is called during GC collection, but can be also called manually. 188 void dispose() 189 { 190 if (_cl) 191 { 192 n.sc_compiler_delete(_cl); 193 _cl = null; 194 } 195 } 196 197 /// After parsing, API users can modify the SPIR-V via reflection and call this 198 /// to disassemble the SPIR-V into the desired langauage. 199 /// Sub-classes actually implement this. 200 string compile() 201 { 202 string result; 203 scEnforce(_cl, n.sc_compiler_compile(_cl, result)); 204 return result; 205 } 206 207 /// Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. 208 string getName(uint id) const 209 { 210 string result; 211 scEnforce(_cl, n.sc_compiler_get_name(_cl, id, result)); 212 return result; 213 } 214 215 /// Applies a decoration to an ID. Effectively injects OpDecorate. 216 void setDecoration(uint id, spv.Decoration decoration, uint argument = 0) 217 { 218 scEnforce(_cl, n.sc_compiler_set_decoration(_cl, id, decoration, argument)); 219 } 220 221 /// ditto 222 void setDecorationString(uint id, spv.Decoration decoration, string argument) 223 { 224 scEnforce(_cl, n.sc_compiler_set_decoration_string(_cl, id, decoration, argument)); 225 } 226 227 /// Overrides the identifier OpName of an ID. 228 /// Identifiers beginning with underscores or identifiers which contain double underscores 229 /// are reserved by the implementation. 230 void setName(uint id, string name) 231 { 232 scEnforce(_cl, n.sc_compiler_set_name(_cl, id, name)); 233 } 234 235 // const Bitset &get_decoration_bitset(uint id) const; 236 237 /// Returns whether the decoration has been applied to the ID. 238 bool hasDecoration(uint id, spv.Decoration decoration) const 239 { 240 bool result = void; 241 scEnforce(_cl, n.sc_compiler_has_decoration(_cl, id, decoration, result)); 242 return result; 243 } 244 245 /// Gets the value for decorations which take arguments. 246 /// If the decoration is a boolean (i.e. spv.DecorationNonWritable), 247 /// 1 will be returned. 248 /// If decoration doesn't exist or decoration is not recognized, 249 /// 0 will be returned. 250 uint getDecoration(uint id, spv.Decoration decoration) const 251 { 252 uint result = void; 253 scEnforce(_cl, n.sc_compiler_get_decoration(_cl, id, decoration, result)); 254 return result; 255 } 256 /// ditto 257 string getDecorationString(uint id, spv.Decoration decoration) const 258 { 259 string result = void; 260 scEnforce(_cl, n.sc_compiler_get_decoration_string(_cl, id, decoration, result)); 261 return result; 262 } 263 264 /// Removes the decoration for an ID. 265 void unsetDecoration(uint id, spv.Decoration decoration) 266 { 267 scEnforce(_cl, n.sc_compiler_unset_decoration(_cl, id, decoration)); 268 } 269 270 /// Gets the SPIR-V type associated with ID. 271 /// Mostly used with Resource::typeId and Resource::baseTypeId to parse the underlying type of a resource. 272 const(SPIRType)* getType(uint id) const 273 { 274 const(SPIRType)* result = void; 275 scEnforce(_cl, n.sc_compiler_get_type(_cl, id, result)); 276 return result; 277 } 278 279 /// Gets the SPIR-V type of a variable. 280 const(SPIRType)* getTypeFromVariable(uint id) const 281 { 282 const(SPIRType)* result = void; 283 scEnforce(_cl, n.sc_compiler_get_type_from_variable(_cl, id, result)); 284 return result; 285 } 286 287 /// Gets the id of SPIR-V type underlying the given typeId, which might be a pointer. 288 uint getNonPointerTypeId(uint typeId) const 289 { 290 uint result = void; 291 scEnforce(_cl, n.sc_compiler_get_non_pointer_type_id(_cl, typeId, result)); 292 return result; 293 } 294 295 /// Gets the SPIR-V type underlying the given typeId, which might be a pointer. 296 const(SPIRType)* getNonPointerType(uint typeId) const 297 { 298 const(SPIRType)* result = void; 299 scEnforce(_cl, n.sc_compiler_get_non_pointer_type(_cl, typeId, result)); 300 return result; 301 } 302 303 // is_sampled_image 304 305 /// Gets the underlying storage class for an OpVariable. 306 spv.StorageClass getStorageClass(uint id) const 307 { 308 spv.StorageClass result = void; 309 scEnforce(_cl, n.sc_compiler_get_storage_class(_cl, id, result)); 310 return result; 311 } 312 313 /// If get_name() is an empty string, get the fallback name which will be used 314 /// instead in the disassembled source. 315 string getFallbackName(uint id) const 316 { 317 string result = void; 318 scEnforce(_cl, n.sc_compiler_get_fallback_name(_cl, id, result)); 319 return result; 320 } 321 322 /// If get_name() of a Block struct is an empty string, get the fallback name. 323 /// This needs to be per-variable as multiple variables can use the same block type. 324 string getBlockFallbackName(uint id) const 325 { 326 string result = void; 327 scEnforce(_cl, n.sc_compiler_get_block_fallback_name(_cl, id, result)); 328 return result; 329 } 330 331 /// Given an OpTypeStruct in ID, obtain the identifier for member number "index". 332 /// This may be an empty string. 333 string getMemberName(uint id, uint index) const 334 { 335 string result = void; 336 scEnforce(_cl, n.sc_compiler_get_member_name(_cl, id, index, result)); 337 return result; 338 } 339 340 /// Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". 341 uint getMemberDecoration(uint id, uint index, spv.Decoration decoration) const 342 { 343 uint result = void; 344 scEnforce(_cl, n.sc_compiler_get_member_decoration(_cl, id, index, decoration, result)); 345 return result; 346 } 347 /// ditto 348 string getMemberDecorationString(uint id, uint index, spv.Decoration decoration) const 349 { 350 string result = void; 351 scEnforce(_cl, n.sc_compiler_get_member_decoration_string(_cl, id, 352 index, decoration, result)); 353 return result; 354 } 355 356 /// Sets the member identifier for OpTypeStruct ID, member number "index". 357 void setMemberName(uint id, uint index, string name) 358 { 359 scEnforce(_cl, n.sc_compiler_set_member_name(_cl, id, index, name)); 360 } 361 362 /// Returns the qualified member identifier for OpTypeStruct ID, member number "index", 363 /// or an empty string if no qualified alias exists 364 string getMemberQualifiedName(uint type_id, uint index) const 365 { 366 string result = void; 367 scEnforce(_cl, n.sc_compiler_get_member_qualified_name(_cl, type_id, index, result)); 368 return result; 369 } 370 371 /// Sets the qualified member identifier for OpTypeStruct ID, member number "index". 372 void setMemberQualifiedName(uint type_id, uint index, string name) 373 { 374 scEnforce(_cl, n.sc_compiler_set_member_qualified_name(_cl, type_id, index, name)); 375 } 376 377 // const Bitset &get_member_decoration_bitset(uint id, uint index) const; 378 379 /// Returns whether the decoration has been applied to a member of a struct. 380 bool hasMemberDecoration(uint id, uint index, spv.Decoration decoration) const 381 { 382 bool result = void; 383 scEnforce(_cl, n.sc_compiler_has_member_decoration(_cl, id, index, decoration, result)); 384 return result; 385 } 386 387 /// Similar to setDecoration, but for struct members. 388 void setMemberDecoration(uint id, uint index, spv.Decoration decoration, uint argument = 0) 389 { 390 scEnforce(_cl, n.sc_compiler_set_member_decoration(_cl, id, index, decoration, argument)); 391 } 392 393 void setMemberDecorationString(uint id, uint index, 394 spv.Decoration decoration, string argument) 395 { 396 scEnforce(_cl, n.sc_compiler_set_member_decoration_string(_cl, id, 397 index, decoration, argument)); 398 } 399 400 /// Unsets a member decoration, similar to unsetDecoration. 401 void unsetMemberDecoration(uint id, uint index, spv.Decoration decoration) 402 { 403 scEnforce(_cl, n.sc_compiler_unset_member_decoration(_cl, id, index, decoration)); 404 } 405 406 /// Gets the fallback name for a member, similar to getFallbackName. 407 string getFallbackMemberName(uint index) const 408 { 409 import std.array : array; 410 import std.range : repeat; 411 412 return repeat('_', index).array; 413 } 414 415 /// Returns a vector of which members of a struct are potentially in use by a 416 /// SPIR-V shader. The granularity of this analysis is per-member of a struct. 417 /// This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. 418 /// ID is the Resource::id obtained from get_shader_resources(). 419 BufferRange[] getActiveBufferRanges(uint id) const 420 { 421 BufferRange[] result = void; 422 scEnforce(_cl, n.sc_compiler_get_active_buffer_ranges(_cl, id, result)); 423 return result; 424 } 425 426 /// Returns the effective size of a buffer block. 427 size_t getDeclaredStructSize(const(SPIRType)* struct_type) const 428 { 429 size_t result = void; 430 scEnforce(_cl, n.sc_compiler_get_declared_struct_size(_cl, struct_type, result)); 431 return result; 432 }; 433 434 /// Returns the effective size of a buffer block, with a given array size 435 /// for a runtime array. 436 /// SSBOs are typically declared as runtime arrays. getDeclaredStructSize() will return 0 for the size. 437 /// This is not very helpful for applications which might need to know the array stride of its last member. 438 /// This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function 439 /// to query the size of the buffer, assuming that the last member has a certain size. 440 /// If the buffer does not contain a runtime array, arraySize is ignored, and the function will behave as 441 /// getDeclaredStructSize(). 442 /// To get the array stride of the last member, something like: 443 /// getDeclaredStructSizeRuntimeArray(type, 1) - getDeclaredStructSizeRuntimeArray(type, 0) will work. 444 size_t getDeclaredStructSizeRuntimeArray(const(SPIRType)* structType, size_t arraySize) const 445 { 446 size_t result = void; 447 scEnforce(_cl, n.sc_compiler_get_declared_struct_size_runtime_array(_cl, 448 structType, arraySize, result)); 449 return result; 450 } 451 452 /// Returns the effective size of a buffer block struct member. 453 size_t getDeclaredStructMemberSize(const(SPIRType)* structType, uint index) const 454 { 455 size_t result = void; 456 scEnforce(_cl, n.sc_compiler_get_declared_struct_member_size(_cl, 457 structType, index, result)); 458 return result; 459 } 460 461 /// Returns a set of all global variables which are statically accessed 462 /// by the control flow graph from the current entry point. 463 /// Only variables which change the interface for a shader are returned, that is, 464 /// variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter 465 /// storage classes are returned. 466 /// 467 /// To use the returned set as the filter for which variables are used during compilation, 468 /// this set can be moved to setEnabledInterfaceVariables(). 469 uint[] getActiveInterfaceVariables() const 470 { 471 uint[] result = void; 472 scEnforce(_cl, n.sc_compiler_get_active_interface_variables(_cl, result)); 473 return result; 474 } 475 476 /// Sets the interface variables which are used during compilation. 477 /// By default, all variables are used. 478 /// Once set, compile() will only consider the set in active_variables. 479 void setEnabledInterfaceVariables(const(uint)[] activeVariables) 480 { 481 scEnforce(_cl, n.sc_compiler_set_enabled_interface_variables(_cl, activeVariables)); 482 } 483 484 /// Query shader resources, use ids with reflection interface to modify or query binding points, etc. 485 ShaderResources getShaderResources() const 486 { 487 ShaderResources result = void; 488 scEnforce(_cl, n.sc_compiler_get_shader_resources(_cl, result)); 489 return result; 490 } 491 492 /// Query shader resources, but only return the variables which are part of active_variables. 493 /// E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically 494 /// accessed. 495 ShaderResources getShaderResources(const(uint)[] activeVariables) const 496 { 497 ShaderResources result = void; 498 scEnforce(_cl, n.sc_compiler_get_shader_resources_for_vars(_cl, activeVariables, result)); 499 return result; 500 } 501 502 /// Remapped variables are considered built-in variables and a backend will 503 /// not emit a declaration for this variable. 504 /// This is mostly useful for making use of builtins which are dependent on extensions. 505 void setRemappedVariableState(uint id, bool remapEnable) 506 { 507 scEnforce(_cl, n.sc_compiler_set_remapped_variable_state(_cl, id, remapEnable)); 508 } 509 /// ditto 510 bool getRemappedVariableState(uint id) const 511 { 512 bool result = void; 513 scEnforce(_cl, n.sc_compiler_get_remapped_variable_state(_cl, id, result)); 514 return result; 515 } 516 517 /// For subpassInput variables which are remapped to plain variables, 518 /// the number of components in the remapped 519 /// variable must be specified as the backing type of subpass inputs are opaque. 520 void setSubpassInputRemappedComponents(uint id, uint components) 521 { 522 scEnforce(_cl, n.sc_compiler_set_subpass_input_remapped_components(_cl, id, components)); 523 } 524 /// ditto 525 uint getSubpassInputRemappedComponents(uint id) const 526 { 527 uint result = void; 528 scEnforce(_cl, n.sc_compiler_get_subpass_input_remapped_components(_cl, id, result)); 529 return result; 530 } 531 532 /// New variants of entry point query and reflection. 533 /// Names for entry points in the SPIR-V module may alias if they belong to different execution models. 534 /// To disambiguate, we must pass along with the entry point names the execution model. 535 EntryPoint[] getEntryPointsAndStages() const 536 { 537 EntryPoint[] result = void; 538 scEnforce(_cl, n.sc_compiler_get_entry_points_and_stages(_cl, result)); 539 return result; 540 } 541 542 /// ditto 543 void setEntryPoint(string entry, spv.ExecutionModel executionModel) 544 { 545 scEnforce(_cl, n.sc_compiler_set_entry_point(_cl, entry, executionModel)); 546 } 547 548 /// ditto 549 void renameEntryPoint(string old_name, string new_name, spv.ExecutionModel executionModel) 550 { 551 scEnforce(_cl, n.sc_compiler_rename_entry_point(_cl, old_name, new_name, executionModel)); 552 } 553 554 /// ditto 555 string getCleansedEntryPointName(string name, spv.ExecutionModel executionModel) const 556 { 557 string result = void; 558 scEnforce(_cl, n.sc_compiler_get_cleansed_entry_point_name(_cl, name, 559 executionModel, result)); 560 return result; 561 } 562 563 // const Bitset &get_execution_mode_bitset() const; 564 565 void unsetExecutionMode(spv.ExecutionMode mode) 566 { 567 scEnforce(_cl, n.sc_compiler_unset_execution_mode(_cl, mode)); 568 } 569 570 void setExecutionMode(spv.ExecutionMode mode, uint arg0 = 0, uint arg1 = 0, uint arg2 = 0) 571 { 572 scEnforce(_cl, n.sc_compiler_set_execution_mode(_cl, mode, arg0, arg1, arg2)); 573 } 574 575 /// Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). 576 /// For LocalSize, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). 577 /// For execution modes which do not have arguments, 0 is returned. 578 uint getExecutionModeArgument(spv.ExecutionMode mode, uint index = 0) const 579 { 580 uint result = void; 581 scEnforce(_cl, n.sc_compiler_get_execution_mode_argument(_cl, mode, index, result)); 582 return result; 583 } 584 /// ditto 585 spv.ExecutionModel getExecutionModel() const 586 { 587 spv.ExecutionModel result = void; 588 scEnforce(_cl, n.sc_compiler_get_execution_model(_cl, result)); 589 return result; 590 } 591 592 /// In SPIR-V, the compute work group size can be represented by a constant vector, in which case 593 /// the LocalSize execution mode is ignored. 594 /// 595 /// This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. 596 /// To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified 597 /// directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. 598 /// 599 /// To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. 600 /// Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. 601 /// NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, 602 /// while in this interface, LocalSize is only ignored for specialization constants. 603 /// 604 /// The specialization constant will be written to x, y and z arguments. 605 /// If the component is not a specialization constant, a zeroed out struct will be written. 606 /// The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful 607 /// for most use cases. 608 uint getWorkGroupSizeSpecializationConstants(out SpecializationConstant x, 609 out SpecializationConstant y, out SpecializationConstant z) const 610 { 611 uint result = void; 612 scEnforce(_cl, n.sc_compiler_get_work_group_size_specialization_constants(_cl, 613 x, y, z, result)); 614 return result; 615 } 616 617 /// Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where 618 /// said instruction is used without a combined image sampler. 619 /// GLSL targets do not support the use of texelFetch without a sampler. 620 /// To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of 621 /// texelFetch as necessary. 622 /// 623 /// This must be called before buildCombinedImageSamplers(). 624 /// buildCombinedImageSamplers() may refer to the ID returned by this method if the returned ID is non-zero. 625 /// The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object 626 /// is required. 627 /// 628 /// If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). 629 /// Calling this function also invalidates getActiveInterfaceVariables(), so this should be called 630 /// before that function. 631 uint buildDummySamplerForCombinedImages() 632 { 633 uint result = void; 634 scEnforce(_cl, n.sc_compiler_build_dummy_sampler_for_combined_images(_cl, result)); 635 return result; 636 } 637 638 /// Analyzes all separate image and samplers used from the currently selected entry point, 639 /// and re-routes them all to a combined image sampler instead. 640 /// This is required to "support" separate image samplers in targets which do not natively support 641 /// this feature, like GLSL/ESSL. 642 /// 643 /// This must be called before compile() if such remapping is desired. 644 /// This call will add new sampled images to the SPIR-V, 645 /// so it will appear in reflection if getShaderResources() is called after buildCombinedImageSamplers. 646 /// 647 /// If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, 648 /// but will still appear in reflection. 649 /// 650 /// The resulting samplers will be void of any decorations like name, descriptor sets and binding points, 651 /// so this can be added before compile() if desired. 652 /// 653 /// Combined image samplers originating from this set are always considered active variables. 654 /// Arrays of separate samplers are not supported, but arrays of separate images are supported. 655 /// Array of images + sampler -> Array of combined image samplers. 656 void buildCombinedImageSamplers() 657 { 658 scEnforce(_cl, n.sc_compiler_build_combined_image_samples(_cl)); 659 } 660 661 /// Gets a remapping for the combined image samplers. 662 CombinedImageSampler[] getCombinedImageSamplers() const 663 { 664 CombinedImageSampler[] result = void; 665 scEnforce(_cl, n.sc_compiler_get_combined_image_samplers(_cl, result)); 666 return result; 667 } 668 669 // void set_variable_type_remap_callback(VariableTypeRemapCallback cb) 670 671 /// API for querying which specialization constants exist. 672 /// To modify a specialization constant before compile(), use get_constant(constant.id), 673 /// then update constants directly in the SPIRConstant data structure. 674 /// For composite types, the subconstants can be iterated over and modified. 675 /// constant_type is the SPIRType for the specialization constant, 676 /// which can be queried to determine which fields in the unions should be poked at. 677 SpecializationConstant[] getSpecializationConstants() const 678 { 679 SpecializationConstant[] result = void; 680 scEnforce(_cl, n.sc_compiler_get_specialization_constants(_cl, result)); 681 return result; 682 } 683 684 // SPIRConstant &get_constant(uint id); 685 // const SPIRConstant &get_constant(uint id) const; 686 687 uint getCurrentIdBound() const 688 { 689 uint result = void; 690 scEnforce(_cl, n.sc_compiler_get_current_id_bound(_cl, result)); 691 return result; 692 } 693 694 /// API for querying buffer objects. 695 /// The type passed in here should be the base type of a resource, i.e. 696 /// getType(resource.baseTypeId) 697 /// as decorations are set in the basic Block type. 698 /// The type passed in here must have these decorations set, or an exception is raised. 699 /// Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. 700 uint typeStructMemberOffset(const(SPIRType)* type, uint index) const 701 { 702 uint result = void; 703 scEnforce(_cl, n.sc_compiler_type_struct_member_offset(_cl, type, index, result)); 704 return result; 705 } 706 707 /// ditto 708 uint typeStructMemberArrayStride(const(SPIRType)* type, uint index) const 709 { 710 uint result = void; 711 scEnforce(_cl, n.sc_compiler_type_struct_member_array_stride(_cl, type, index, result)); 712 return result; 713 } 714 715 /// ditto 716 uint typeStructMemberMatrixStride(const(SPIRType)* type, uint index) const 717 { 718 uint result = void; 719 scEnforce(_cl, n.sc_compiler_type_struct_member_matrix_stride(_cl, type, index, result)); 720 return result; 721 } 722 723 /// Gets the offset in SPIR-V words (uint) for a decoration which was originally declared in the SPIR-V binary. 724 /// The offset will point to one or more uint literals which can be modified in-place before using the SPIR-V binary. 725 /// Note that adding or removing decorations using the reflection API will not change the behavior of this function. 726 /// If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, 727 /// otherwise, returns false. 728 /// If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. 729 bool getBinaryOffsetForDecoration(uint id, spv.Decoration decoration, out uint word_offset) const 730 { 731 bool result = void; 732 scEnforce(_cl, n.sc_compiler_get_binary_offset_for_decoration(_cl, id, 733 decoration, word_offset, result)); 734 return result; 735 } 736 737 /// HLSL counter buffer reflection interface. 738 /// Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where 739 /// one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. 740 /// To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier 741 /// which lets us link the two buffers together. 742 /// 743 /// Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. 744 /// 745 /// If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. 746 /// Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will 747 /// only return true if OpSource was reported HLSL. 748 /// To rely on this functionality, ensure that the SPIR-V module is not stripped. 749 bool bufferIsHlslCounterBuffer(uint id) const 750 { 751 bool result = void; 752 scEnforce(_cl, n.sc_compiler_buffer_is_hlsl_counter_buffer(_cl, id, result)); 753 return result; 754 } 755 756 /// Queries if a buffer object has a neighbor "counter" buffer. 757 /// If so, the ID of that counter buffer will be returned in counter_id. 758 /// If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. 759 /// Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will 760 /// only return true if OpSource was reported HLSL. 761 /// To rely on this functionality, ensure that the SPIR-V module is not stripped. 762 bool bufferGetHlslCounterBuffer(uint id, out uint counterId) 763 { 764 bool result = void; 765 scEnforce(_cl, n.sc_compiler_buffer_get_hlsl_counter_buffer(_cl, id, counterId, result)); 766 return result; 767 } 768 769 /// Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. 770 spv.Capability[] getDeclaredCapabilities() const 771 { 772 spv.Capability[] result = void; 773 scEnforce(_cl, n.sc_compiler_get_declared_capabilities(_cl, result)); 774 return result; 775 } 776 777 /// Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. 778 string[] getDeclaredExtensions() const 779 { 780 string[] result = void; 781 scEnforce(_cl, n.sc_compiler_get_declared_extensions(_cl, result)); 782 return result; 783 } 784 785 /// When declaring buffer blocks in GLSL, the name declared in the GLSL source 786 /// might not be the same as the name declared in the SPIR-V module due to naming conflicts. 787 /// In this case, SPIRV-Cross needs to find a fallback-name, and it might only 788 /// be possible to know this name after compiling to GLSL. 789 /// This is particularly important for HLSL input and UAVs which tends to reuse the same block type 790 /// for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself 791 /// because it might be unique. Instead, you can use this interface to check after compilation which 792 /// name was actually used if your input SPIR-V tends to have this problem. 793 /// For other names like remapped names for variables, etc, it's generally enough to query the name of the variables 794 /// after compiling, block names are an exception to this rule. 795 /// ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. 796 /// 797 /// This also applies to HLSL cbuffers. 798 string getRemappedDeclaredBlockName(uint id) const 799 { 800 string result = void; 801 scEnforce(_cl, n.sc_compiler_get_remapped_declared_block_name(_cl, id, result)); 802 return result; 803 } 804 805 //Bitset get_buffer_block_flags(uint id) const; 806 } 807 808 /// Compiler that produces Glsl code 809 class ScCompilerGlsl : ScCompiler 810 { 811 812 private @property inout(n.ScCompilerGlsl)* glsl() inout 813 { 814 return cast(inout(n.ScCompilerGlsl)*) _cl; 815 } 816 817 this(in uint[] ir) 818 { 819 n.ScCompilerGlsl* cl; 820 string msg; 821 const res = n.sc_compiler_glsl_new(ir, n.gcCallbacks, cl, msg); 822 scEnforce(res, msg); 823 super(cast(n.ScCompiler*) cl); 824 } 825 826 @property ScOptionsGlsl options() const 827 { 828 ScOptionsGlsl opts; 829 n.sc_compiler_glsl_get_options(glsl, opts); 830 return opts; 831 } 832 833 @property void options(ScOptionsGlsl opts) 834 { 835 n.sc_compiler_glsl_set_options(glsl, &opts); 836 } 837 838 /// Returns the current string held in the conversion buffer. Useful for 839 /// capturing what has been converted so far when compile() throws an error. 840 string getPartialSource() 841 { 842 string result = void; 843 scEnforce(_cl, n.sc_compiler_glsl_get_partial_source(glsl, result)); 844 return result; 845 } 846 847 /// Adds a line to be added right after #version in GLSL backend. 848 /// This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross. 849 /// This can be combined with variable remapping. 850 /// A new-line will be added. 851 /// 852 /// While addHeaderLine() is a more generic way of adding arbitrary text to the header 853 /// of a GLSL file, requireExtension() should be used when adding extensions since it will 854 /// avoid creating collisions with SPIRV-Cross generated extensions. 855 /// 856 /// Code added via add_header_line() is typically backend-specific. 857 void addHeaderLine(string str) 858 { 859 scEnforce(_cl, n.sc_compiler_glsl_add_header_line(glsl, str)); 860 } 861 862 /// Adds an extension which is required to run this shader, e.g. 863 /// require_extension("GL_KHR_my_extension"); 864 void requireExtension(string ext) 865 { 866 scEnforce(_cl, n.sc_compiler_glsl_require_extension(glsl, ext)); 867 } 868 869 /// Legacy GLSL compatibility method. 870 /// Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead. 871 /// For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but 872 /// mixing int and float is not. 873 /// The name of the uniform array will be the same as the interface block name. 874 void flattenBufferBlock(uint id) 875 { 876 scEnforce(_cl, n.sc_compiler_glsl_flatten_buffer_block(glsl, id)); 877 } 878 879 }